package com.lagou.factory;

import com.lagou.annotion.MyAutowired;
import com.lagou.annotion.MyService;
import com.lagou.annotion.MyTransactional;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @Author ZHAO Yudong
 * @Date 2020/11/6 14:37
 * @description：
 */
@Slf4j
public class BeanFactory {


    // 存储对象
    private static Map<String, Object> map = new HashMap<>();

    static {
        try {
            //扫描包，分析需实例化的对象列表（service和事务管理器）,通过反射技术实例化对象并且存储待用（map集合）
            //扫描获取反射对象集合
            Reflections f = new Reflections("com.lagou");
            //获取注解为service的集合
            Set<Class<?>> set = f.getTypesAnnotatedWith(MyService.class);
            log.info("扫描MyService注解...");
            for (Class<?> c : set) {
                // 通过反射技术实例化对象
                Object bean = c.newInstance();
                MyService annotation = c.getAnnotation(MyService.class);

                //对象ID在service注解有value时用value，没有时用类名
                if (StringUtils.isEmpty(annotation.value())) {
                    //由于getName获取的是全限定类名，所以要分割去掉前面包名部分
                    String[] names = c.getName().split("\\.");
                    map.put(names[names.length - 1], bean);
                    log.info("放入容器：{}", bean);
                } else {
                    map.put(annotation.value(), bean);
                    log.info("放入容器：{}", bean);
                }
            }
            // 实例化完成之后维护对象的依赖关系，检查哪些对象需要传值进入，根据它的配置，我们传入相应的值
            log.info("开始注入...");
            for (Map.Entry<String, Object> a : map.entrySet()) {
                Object o = a.getValue();
                Class c = o.getClass();
                //log.info("class = {}", c);//当前类
                //获取属性集合
                Field[] fields = c.getDeclaredFields();
                //遍历属性，若持有Autowired注解则注入
                for (Field field : fields) {
                    if (field.isAnnotationPresent(MyAutowired.class)
                            && field.getAnnotation(MyAutowired.class).value()) {
                        String[] names = field.getType().getName().split("\\.");
                        String name = names[names.length - 1];
                        Method[] methods = c.getMethods();
                        for (int j = 0; j < methods.length; j++) {
                            Method method = methods[j];
                            // 该方法就是 setAccountDao(AccountDao accountDao)
                            if (method.getName().equalsIgnoreCase("set" + name)) {
                                log.info("set注入： {}.{}", c.getName(), methods[j].getName());
                                method.invoke(o, map.get(name));
                            }
                        }
                    }
                }
                //判断对象类是否持有Transactional注解，若有则修改对象为代理对象
                if (c.isAnnotationPresent(MyTransactional.class)) {
                    //获取代理工厂
                    ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
                    //判断对象是否实现接口
                    //获取类c实现的所有接口
                    Class[] face = c.getInterfaces();
                    if (face != null && face.length > 0) {
                        log.info("jdk = {}", face);
                        //实现使用JDK
                        o = proxyFactory.getJdkProxy(o);
                    } else {
                        log.info("cglib = {}", face);
                        //没实现使用CGLIB
                        o = proxyFactory.getCglibProxy(o);
                    }
                }
                // 把处理之后的object重新放到map中
                log.info("把处理之后的对象重新放到容器：{}", o.getClass().getName());
                map.put(a.getKey(), o);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public static Object getBean(String id) {
        log.info("从容器中取出： {}", map.get(id));
        return map.get(id);
    }

}
